mod utils;
use anyhow::bail;
use utils::ApiResponse;

use anyhow::{Result, anyhow};
use chrono::{DateTime, Duration, Utc};
use futures::{StreamExt, TryStreamExt, future::BoxFuture};
use reqwest::{Method, header, multipart};
use serde::{Deserialize, Serialize};
use std::{
    collections::HashMap,
    env,
    io::SeekFrom,
    path::{Path, PathBuf},
    sync::Arc,
};
use tokio::{
    fs,
    io::{AsyncReadExt, AsyncSeekExt},
    sync::RwLock,
};

// ----------------------
// 1. 常量与配置
// ----------------------
const CHUNK_SIZE: usize = 10 * 1024 * 1024; // 10MB

// ----------------------
// 1. 数据模型定义
// ----------------------
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct Upload {
    #[serde(rename = "fileID")]
    pub file_id: i64,
    #[serde(rename = "completed")]
    pub completed: bool,
}

// TokenData 需要同时支持 Deserialize (从API解析) 和 Serialize (存文件)
// Clone 用于在内存中复制，Debug 用于打印
#[derive(Deserialize, Serialize, Debug, Clone)]
struct TokenData {
    #[serde(rename = "accessToken")]
    access_token: String,

    // chrono 默认支持 ISO 8601 格式 (如 "2025-03-23T15:48:37+08:00")
    // 自动反序列化时会将带时区的时间转换为 UTC
    #[serde(rename = "expiredAt")]
    expired_at: DateTime<Utc>,
}

impl TokenData {
    // 判断 Token 是否有效（预留 60 秒缓冲期）
    fn is_valid(&self) -> bool {
        self.expired_at > Utc::now() + Duration::seconds(60)
    }
}

// 内部缓存的 Token 状态
// 简化：直接持有 Option<TokenData>
#[derive(Debug, Clone)]
struct TokenState {
    data: Option<TokenData>,
}

impl TokenState {
    fn new() -> Self {
        Self { data: None }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CreateUploadResp {
    /// 文件 ID - 当文件已存在（秒传）时会返回
    #[serde(rename = "fileID")]
    pub file_id: i64,

    /// 预上传 ID - 分片上传必需，如果秒传成功则可能不存在
    #[serde(rename = "preuploadID")]
    pub preupload_id: String,

    /// 是否秒传 - true 表示文件已在云端，直接完成上传
    pub reuse: bool,

    /// 每个分片大小（单位: 字节），必须按此大小切分文件
    #[serde(rename = "sliceSize")]
    pub slice_size: usize,

    /// 上传地址列表 - 一般只在特殊传输模式下使用，常规流程是通过 get_upload_url 获取单片地址
    pub servers: Vec<String>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct FileListData {
    #[serde(rename = "fileList")]
    pub file_list: Vec<FileInfo>,

    /// -1代表最后一页（无需再翻页查询）,其他代表下一页开始的文件id，携带到请求参数中
    #[serde(rename = "lastFileId")]
    pub last_file_id: i32,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct FileNode {
    #[serde(flatten)]
    pub info: FileInfo,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub children: Vec<FileNode>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct FileInfo {
    // ID 通常很大，使用 i64 防止溢出
    #[serde(rename = "fileId", alias = "fileID")]
    pub file_id: i64,

    // --- 关键修正 ---
    // JSON 返回的是 "filename"，不是 "fileName"
    #[serde(rename = "filename")]
    pub file_name: String,

    // 0-文件 1-文件夹
    // 建议用 i32，比 u8 更通用，防止越界
    #[serde(rename = "type")]
    pub file_type: i32,

    // 文件大小，必须用 i64
    #[serde(rename = "size")]
    pub size: i64,

    // Etag 有时可能为 null (特别是文件夹)，必须用 Option
    #[serde(rename = "etag")]
    pub etag: String,

    // 状态
    #[serde(rename = "status")]
    pub status: i32,

    // 父目录 ID
    #[serde(rename = "parentFileId", alias = "parentFileID")]
    pub parent_file_id: i64,

    // 分类可能为 null
    #[serde(rename = "category")]
    pub category: Option<i32>,

    // 是否回收站 (0-否/1-是)
    #[serde(rename = "trashed")]
    pub trashed: i32,

    // --- 下面是 JSON 中新出现的字段，建议加上 Option 以防旧数据没有 ---
    #[serde(rename = "createAt")]
    pub create_at: Option<String>,

    #[serde(rename = "updateAt")]
    pub update_at: Option<String>,

    #[serde(rename = "punishFlag")]
    pub punish_flag: Option<i32>,
}

// ----------------------
// 2. 核心 Client 结构体
// ----------------------

#[derive(Clone)]
pub struct PanClient {
    client_id: String,
    client_secret: String,
    http_client: reqwest::Client,
    // 使用 Arc<RwLock<...>> 实现跨线程/异步任务的安全共享和修改
    token_state: Arc<RwLock<TokenState>>,
    token_file_path: PathBuf,
}

impl PanClient {
    const BASE_URL: &'static str = "https://open-api.123pan.com";
    /// 初始化客户端
    pub fn new(client_id: String, client_secret: String) -> Self {
        // 默认的 HTTP 客户端配置
        let mut headers = header::HeaderMap::new();
        headers.insert("Platform", header::HeaderValue::from_static("open_platform"));

        let http_client = reqwest::Client::builder()
            .default_headers(headers)
            .build()
            .unwrap_or_default();

        // --- 新增逻辑开始 ---
        // 获取系统临时目录
        let mut temp_path = env::temp_dir();
        // 拼接文件名，例如在 Windows 下可能是 C:\Users\Admin\AppData\Local\Temp\123pan_token.json
        // 在 Linux 下可能是 /tmp/123pan_token.json
        temp_path.push("123pan_token.json");

        Self {
            client_id,
            client_secret,
            http_client,
            token_state: Arc::new(RwLock::new(TokenState::new())),
            token_file_path: temp_path, // 存入结构体
        }
    }

    /// 核心方法：获取有效的 Access Token
    /// 如果 Token 不存在或过期，会自动请求 API 刷新
    async fn get_access_token(&self) -> anyhow::Result<String> {
        // 1. 尝试获取读锁，检查是否有缓存且有效的 Token
        {
            let state = self.token_state.read().await;
            if let Some(data) = &state.data {
                if data.is_valid() {
                    return Ok(data.access_token.clone());
                }
            }
        }

        // 2. 准备更新：获取写锁
        // 注意：这里需要 Double-check，因为在获取写锁等待期间，可能有其他线程更新了 Token
        let mut state = self.token_state.write().await;

        // 2.1 Double-check 内存
        if let Some(data) = &state.data {
            if data.is_valid() {
                return Ok(data.access_token.clone());
            }
        }

        // 2.2 第二层：尝试从本地文件加载
        // 只有当内存中没有或过期时，才尝试读文件
        // println!("Checking local token file...");
        if let Ok(file_token) = self.load_token_from_disk().await {
            if file_token.is_valid() {
                // println!("Loaded valid token from disk.");
                // 更新内存缓存
                state.data = Some(file_token.clone());
                return Ok(file_token.access_token);
            }
        }

        // 2.3 第三层：请求 API 获取新 Token
        // println!("Fetching new token from API...");
        let new_token_data = self.fetch_token_from_api().await?;

        // 2.4 持久化：保存到磁盘
        if let Err(e) = self.save_token_to_disk(&new_token_data).await {
            eprintln!("Warning: Failed to save token to disk: {}", e);
            // 保存失败不影响业务流程，仅打印日志
        }

        // 2.5 更新内存
        state.data = Some(new_token_data.clone());
        Ok(new_token_data.access_token)
    }

    /// 从磁盘加载 Token
    async fn load_token_from_disk(&self) -> anyhow::Result<TokenData> {
        let content = fs::read_to_string(&self.token_file_path).await?;
        let data: TokenData = serde_json::from_str(&content)?;
        Ok(data)
    }

    /// 保存 Token 到磁盘
    async fn save_token_to_disk(&self, data: &TokenData) -> anyhow::Result<()> {
        let content = serde_json::to_string_pretty(data)?;
        fs::write(&self.token_file_path, content).await?;
        println!("Token saved to {:?}", self.token_file_path.display());
        Ok(())
    }

    /// 内部私有方法：调用 123pan 接口获取 Token
    async fn fetch_token_from_api(&self) -> anyhow::Result<TokenData> {
        let url = "https://open-api.123pan.com/api/v1/access_token";

        let body = serde_json::json!({
            "clientID": self.client_id,
            "clientSecret": self.client_secret
        });

        let resp = self
            .http_client
            .post(url)
            .json(&body)
            .send()
            .await?
            .json::<ApiResponse<TokenData>>()
            .await?;

        if resp.code != 0 || resp.data.is_none() {
            return Err(anyhow::format_err!(
                "API Error: code={}, msg={}",
                resp.code,
                resp.message
            ));
        }

        Ok(resp.data.unwrap())
    }

    /// 创建文件记录
    async fn create_file_record(
        &self,
        id: i64,
        filename: &str,
        etag: &str,
        size: usize,
        duplicate: Option<i32>,
    ) -> Result<ApiResponse<CreateUploadResp>> {
        let req = self.request_with_auth(Method::POST, "/upload/v2/file/create").await?;

        let mut form = serde_json::json!({
            "parentFileID": id,
            "filename": filename,
            "etag": etag,
            "size": size
        });

        if let Some(duplicate) = duplicate {
            form["duplicate"] = serde_json::Value::Number(duplicate.into());
        }

        let resp = req.json(&form).send().await?;
        if !resp.status().is_success() {
            return Err(anyhow!("创建文件记录失败: {}", resp.text().await?));
        }

        let json: ApiResponse<CreateUploadResp> = resp.json().await?;
        // let text = resp.text().await?;
        // Ok(serde_json::from_str(&text)?)
        Ok(json)
    }

    /// 上传分片
    async fn upload_slice(
        &self,
        conf: &CreateUploadResp,
        index: usize,
        data: &[u8],
    ) -> Result<ApiResponse<serde_json::Value>> {
        if conf.preupload_id.is_empty() {
            return Err(anyhow::anyhow!("preupload_id is empty"));
        }
        let id = conf.preupload_id.clone();

        let host = conf.servers.first().ok_or_else(|| anyhow!("servers 不能为空"))?;
        let slice_md5 = format!("{:x}", md5::compute(data));

        let url = format!("{}/upload/v2/file/slice", host);
        let req = self.request_with_auth(Method::POST, &url).await?;

        let part = multipart::Part::bytes(data.to_vec())
            .file_name(format!("slice-{}.bin", index)) // 可选，但更规范
            .mime_str("application/octet-stream")?; // 文件通用二进制类型

        let form = multipart::Form::new()
            .text("preuploadID", id)
            .text("sliceNo", index.to_string())
            .text("sliceMD5", slice_md5)
            .part("slice", part); // ⚠️ 改成 slice

        let resp = req
            .multipart(form) // 自动生成 boundary
            .send()
            .await?;

        if !resp.status().is_success() {
            bail!("上传分片失败: {}", resp.text().await?);
        }

        println!("上传分片成功: {}", index);
        Ok(resp.json().await?)
    }

    /// 完成上传
    async fn complete_upload(&self, id: &str) -> Result<ApiResponse<Upload>> {
        let max_retries = 10;
        let url = "/upload/v2/file/upload_complete";
        for attempt in 1..=max_retries {
            let req = self.request_with_auth(Method::POST, url).await?;
            let resp = req.json(&serde_json::json!({ "preuploadID": id })).send().await?;
            if !resp.status().is_success() {
                bail!("第 {} 次完成上传失败: {}", attempt, resp.text().await?);
            }

            let api_resp: ApiResponse<Upload> = resp.json().await?;
            match api_resp.code {
                20103 => {
                    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
                    continue;
                }
                _ => return Ok(api_resp),
            }
        }

        bail!("完成上传超时: 已重试 {} 次，文件仍在校验中", max_retries);
    }

    async fn upload_domain(&self) -> Result<ApiResponse<Vec<String>>> {
        let url = "/upload/v2/file/domain";
        let req = self.request_with_auth(Method::GET, url).await?;
        let resp = req.send().await?;
        if !resp.status().is_success() {
            return Err(anyhow!("获取域名失败: {}", resp.text().await?));
        }
        let json: ApiResponse<Vec<String>> = resp.json().await?;
        Ok(json)
    }

    // ----------------------
    // 3. 业务方法示例
    // ----------------------

    /// 示例：通用的带有认证的请求构建器
    /// 这是一个辅助方法，给所有需要 Token 的请求使用
    pub async fn request_with_auth(
        &self,
        method: reqwest::Method,
        path: &str,
    ) -> anyhow::Result<reqwest::RequestBuilder> {
        let token = self.get_access_token().await?;
        let full_url = if path.starts_with("http://") || path.starts_with("https://") {
            path.to_string()
        } else {
            // 相对路径才拼接 BASE_URL
            format!("{}{}", Self::BASE_URL, path)
        };

        let req = self
            .http_client
            .request(method, &full_url)
            .header("Authorization", format!("Bearer {}", token));

        Ok(req)
    }

    // 注意：返回值变成了 BoxFuture，这是处理异步递归的标准做法
    pub fn list_tree(&self, parent_id: i64) -> BoxFuture<'_, anyhow::Result<Vec<FileNode>>> {
        Box::pin(async move {
            let mut result_nodes = Vec::new();

            // 1. 获取当前层级的所有文件
            // 这里调用你已经写好的 list 函数
            let files = self.list(parent_id).await?;

            for f in files {
                let file_id = f.file_id; // 假设 file_id 是 i32 或 i64
                let is_dir = f.file_type == 1;

                let mut node = FileNode {
                    info: f,
                    children: Vec::new(),
                };

                // 2. 如果是文件夹，递归调用自身
                if is_dir {
                    // await 递归结果并赋值给 children
                    let children = self.list_tree(file_id).await?;
                    node.children = children;
                }

                result_nodes.push(node);
            }

            Ok(result_nodes)
        })
    }

    /// 获取一个文件夹所有文件（递归）
    pub async fn list(&self, parent_id: i64) -> anyhow::Result<Vec<FileInfo>> {
        let mut all_files: Vec<FileInfo> = Vec::new();
        let mut page: i32 = 0;

        loop {
            // 调用单页获取
            let page_data = self.get_file_list_page(parent_id, page).await?;
            // 合并结果
            all_files.extend(page_data.file_list.into_iter().filter(|f| f.trashed != 1));
            // 判断是否还有下一页
            if page_data.last_file_id == -1 {
                break;
            } else {
                page = page_data.last_file_id;
            }
        }

        Ok(all_files)
    }

    /// 获取某个目录的一页文件列表
    async fn get_file_list_page(&self, parent_id: i64, page: i32) -> anyhow::Result<FileListData> {
        let url = "/api/v2/file/list";
        let request = self.request_with_auth(Method::GET, url).await?;
        let resp: reqwest::Response = request
            .query(&[
                ("parentFileId", parent_id),
                ("limit", 100i64),
                ("lastFileId", page.into()), // 用于分页
            ])
            .send()
            .await?;

        let text_body = resp.text().await?;
        let resp_body: ApiResponse<FileListData> = serde_json::from_str(&text_body)?;
        resp_body.into_result()
    }

    /// 获取某个目录的一页文件列表
    async fn get_file_list_page2(&self, query: &str, page: i32) -> anyhow::Result<FileListData> {
        let url = "/api/v2/file/list";
        let request = self.request_with_auth(Method::GET, url).await?;
        let resp: reqwest::Response = request
            .query(&[
                ("parentFileId", 0.to_string()),
                ("searchData", query.to_string()),
                ("searchMode", 0.to_string()),
                ("limit", 100.to_string()),
                ("lastFileId", page.to_string()),
            ])
            .send()
            .await?;

        let text_body = resp.text().await?;
        let resp_body: ApiResponse<FileListData> = serde_json::from_str(&text_body)?;
        resp_body.into_result()
    }

    pub async fn get_file_info(&self, id: i64) -> anyhow::Result<FileInfo> {
        let url = "/api/v1/file/detail";
        let request = self.request_with_auth(Method::GET, url).await?;
        let resp: reqwest::Response = request.query(&[("fileID", id)]).send().await?;

        let text_body = resp.text().await?;
        let resp_body: ApiResponse<FileInfo> = serde_json::from_str(&text_body)?;
        resp_body.into_result()
    }

    /// 通过名字精准搜索文件
    pub async fn search(&self, name: &str, dir: Option<&str>) -> anyhow::Result<Vec<FileInfo>> {
        let mut all_files: Vec<FileInfo> = Vec::new();
        let mut page: i32 = 0;

        loop {
            // 调用单页获取
            let page_data = self.get_file_list_page2(name, page).await?;
            // 合并结果
            all_files.extend(page_data.file_list);
            // 判断是否还有下一页
            if page_data.last_file_id == -1 {
                break;
            } else {
                page = page_data.last_file_id;
            }
        }

        // === 2. 如果没有传 parent_name，直接返回全部 ===
        if dir.is_none() {
            return Ok(all_files);
        }

        let target_parent_name = dir.unwrap();
        let mut filtered: Vec<FileInfo> = Vec::new();

        // 缓存：parent_id -> folder_name
        let mut parent_cache: HashMap<i64, String> = HashMap::new();

        for file in all_files.into_iter() {
            let pid = file.parent_file_id;
            // 先查缓存
            let parent_name = if let Some(name) = parent_cache.get(&pid) {
                name.clone()
            } else {
                // 缓存未命中，才请求 API
                let parent_info = self.get_file_info(pid).await?;
                let name = parent_info.file_name;
                parent_cache.insert(pid, name.clone());
                name
            };

            if parent_name == target_parent_name {
                filtered.push(file);
            }
        }

        Ok(filtered)
    }

    /// 上传文件到指定目录
    /// 上传文件到指定目录
    ///
    /// # Arguments
    ///
    /// * `path` - 要上传的文件路径
    /// * `parent_id` - 目标目录的文件ID
    /// * `duplicate` - 处理重复文件的策略，1: 自动重命名 2: 强制覆盖
    pub async fn upload(&self, path: &str, id: i64, duplicate: Option<i32>) -> anyhow::Result<ApiResponse<Upload>> {
        let path_obj = Path::new(path);
        let filename = Self::validate_filename(path)?;

        // 1. 先只获取文件元数据（不读取内容），判断大小
        let metadata = tokio::fs::metadata(path).await?;
        let size = metadata.len();

        // 如果机器内存很大，可以设为 1GB。
        const MEMORY_THRESHOLD: u64 = 500 * 1024 * 1024;
        if size <= MEMORY_THRESHOLD {
            // 1. 一次性读入内存
            let content = tokio::fs::read(path).await?;
            return self.single_upload(&filename, id, content, duplicate).await;
        } else {
            // ============ 方案 B：大文件（流式模式） ============
            println!("大文件模式，流式处理: {} 大小: {}MB", filename, size / (1024 * 1024));

            let etag = utils::calculate_file_md5(path_obj).await?;
            let create_info = self
                .create_file_record(id, &filename, &etag, size as usize, duplicate)
                .await?;
            if create_info.code != 0 {
                return Err(anyhow!("创建上传记录失败: {}", create_info.message));
            }

            let file_data = create_info.data.ok_or_else(|| anyhow!("返回数据为空"))?;

            // 3. 秒传检查
            if file_data.reuse == true {
                return Ok(ApiResponse {
                    code: 0,
                    message: "秒传成功".to_string(),
                    data: Some(Upload {
                        file_id: file_data.file_id,
                        completed: true,
                    }),
                });
            }

            // 4. 分片上传
            // 关键点：这里不要传 buf (Vec<u8>)，而是传 path，让内部自己去读
            return self.multipart_upload_stream(&file_data, path_obj, size).await;
        }
    }

    /// 单步上传（原来的逻辑抽出来）
    async fn single_upload(
        &self,
        name: &str,
        id: i64,
        buf: Vec<u8>,
        duplicate: Option<i32>,
    ) -> anyhow::Result<ApiResponse<Upload>> {
        let hosts = self.upload_domain().await?;

        // 确保 data 存在且不为空
        let hosts_list = hosts.data.ok_or_else(|| anyhow!("上传域名列表为空"))?;
        let host = hosts_list
            .first()
            .ok_or_else(|| anyhow!("未获取到任何上传域名"))?
            .clone();
        if host.trim().is_empty() {
            return Err(anyhow!("获取的上传域名为空字符串"));
        }

        let url = format!("{}/upload/v2/file/single/create", host);
        let request = self.request_with_auth(Method::POST, url.as_str()).await?;

        let digest = md5::compute(&buf);
        let etag = format!("{:x}", digest);

        let mut form = multipart::Form::new()
            .text("parentFileID", id.to_string())
            .text("filename", name.to_string())
            .text("etag", etag)
            .text("size", buf.len().to_string())
            .part("file", multipart::Part::bytes(buf).file_name(name.to_string()));

        if let Some(dup) = duplicate {
            form = form.text("duplicate", dup.to_string());
        }

        let resp = request.multipart(form).send().await?;
        if !resp.status().is_success() {
            bail!("{}", resp.text().await?);
        }

        Ok(resp.json().await?)
    }

    pub async fn multipart_upload_stream(
        &self,
        conf: &CreateUploadResp,
        path: &Path,
        file_size: u64,
    ) -> anyhow::Result<ApiResponse<Upload>> {
        let slice_size = if conf.slice_size > 0 {
            conf.slice_size
        } else {
            CHUNK_SIZE
        };

        let total_chunks = (file_size as usize + slice_size - 1) / slice_size;
        let path_buf = path.to_path_buf(); // 供闭包使用

        // 并发任务流
        let upload_tasks = futures::stream::iter(0..total_chunks).map(|i| {
            let client = self.clone();
            let path = path_buf.clone();

            async move {
                let slice_no = i + 1;
                let start = (i * slice_size) as u64;
                // 计算当前分片实际大小（处理最后一片）
                let current_slice_size = if start + (slice_size as u64) > file_size {
                    (file_size - start) as usize
                } else {
                    slice_size
                };

                // === 核心差异：每个任务自己打开文件去读 ===
                let mut file = tokio::fs::File::open(&path).await?;
                file.seek(SeekFrom::Start(start)).await?;

                // 只分配当前分片大小的内存 (比如 10MB)
                let mut buffer = vec![0u8; current_slice_size];
                file.read_exact(&mut buffer).await?;

                // 上传
                let slice_info = client.upload_slice(&conf, slice_no, &buffer).await?;
                if slice_info.code != 0 {
                    bail!("分片 {} 上传失败: {}", slice_no, slice_info.message);
                }
                Ok::<(), anyhow::Error>(())
            }
        });

        // 并发执行，buffer_unordered 控制并发数，比如 5
        upload_tasks.buffer_unordered(3).try_collect::<Vec<()>>().await?;

        // 完成
        if conf.preupload_id.is_empty() {
            return Err(anyhow::anyhow!("preupload_id is empty"));
        }

        let id = conf.preupload_id.clone();
        self.complete_upload(&id).await
    }

    fn validate_filename(path: &str) -> Result<String> {
        let filename = Path::new(path)
            .file_name()
            .ok_or_else(|| anyhow!("无法获取文件名"))?
            .to_string_lossy()
            .to_string();

        if filename.len() > 255 {
            bail!("文件名不能超过255字符");
        }
        let invalid_chars = ['\"', '\\', '/', ':', '*', '?', '|', '>', '<'];
        if filename.chars().any(|c| invalid_chars.contains(&c)) {
            bail!("文件名包含非法字符: {}", filename);
        }
        if filename.trim().is_empty() {
            bail!("文件名不能全部是空格");
        }
        Ok(filename)
    }
}
